<?php
/**
 * Created by PhpStorm.
 * User: 程阳
 * Date: 2018/3/20
 * Time: 22:03
 */

namespace app\api\service;


use app\lib\enum\ScopeEnum;
use app\lib\exception\ForbiddenException;
use app\lib\exception\ParamterException;
use app\lib\exception\TokenException;
use think\Cache;
use think\Exception;
use think\Request;

class Token
{
    /**
     * 生成令牌
     * 32为随机字符串 当前毫秒时间戳 自定义的盐  然后md5加密
     * @return string
     */
    public static function generateToken(){
        $randChar = getRandChar(32);
        $timestamp = $_SERVER['REQUEST_TIME_FLOAT'];
        $tokenSalt = config('secure.token_salt');
        return md5($randChar . $timestamp . $tokenSalt);
    }

    /**
     * 验证token是否合法或是否过期，权限为大于等于用户权限
     * @return bool
     * @throws ForbiddenException
     * @throws TokenException
     */
    public static function needPrimaryScope(){
        $scope = self::getCurrentTokenVar('scope');
        if($scope){
            if($scope >= ScopeEnum::User){
                return true;
            }else{
                throw new ForbiddenException();
            }
        }else{
            throw new TokenException();
        }
    }

    /**
     * 用户专有权限验证
     * @return bool
     * @throws ForbiddenException
     * @throws TokenException
     */
    public static function needExclusiveScope(){
        $scope = self::getCurrentTokenVar('scope');
        if($scope){
            if($scope == ScopeEnum::User){
                return true;
            }else{
                throw new ForbiddenException();
            }
        }else{
            throw new TokenException();
        }
    }

    /**
     * 超级管理员权限验证
     * @return bool
     * @throws ForbiddenException
     * @throws TokenException
     */
    public static function needSuperScope(){
        $scope = self::getCurrentTokenVar('scope');
        if($scope){
            if($scope == ScopeEnum::Super){
                return true;
            }else{
                throw new ForbiddenException();
            }
        }else{
            throw new TokenException();
        }
    }

    /**
     * 获取token缓存中对应的数据
     * @param $key
     * @return mixed
     * @throws Exception
     * @throws TokenException
     */
    public static function getCurrentTokenVar($key){
        $token = Request::instance()->header('token');
        $vars = Cache::get($token);
        if(!$vars){
            throw new TokenException();
        }else{
            if(!is_array($vars)){
                $vars = json_decode($vars,true);
            }
            if(array_key_exists($key,$vars)){
                return $vars[$key];
            }else{
                throw new Exception('尝试获取的Token变量并不存在');
            }
        }
    }

    /**
     * 从缓存中获取当前用户
     * @param $keys
     * @return array
     * @throws TokenException
     */
    public static function getCurrentIdentity($keys){
        $token = Request::instance()->header('token');
        $vars = Cache::get($token);
        if(!$vars){
            throw new TokenException();
        }else{
            $vars = json_decode($vars,true);
            $result = [];
            foreach ($keys as $key){
                if(array_key_exists($key,$vars)){
                    $result[$key] = $vars[$key];
                }
            }
            return $result;
        }
    }

    /**
     * 当需要获取全局UID时，应当调用此方法，而不应该自己解析UID
     * @return mixed
     * @throws ParamterException
     */
    public static function getCurrentUid(){
        $scope = self::getCurrentTokenVar('scope');
        if($scope == ScopeEnum::Super){
            // 只有Super权限可以自己传入uid，且必须在get参数中，post不接受任何uid字段
            $userID = input('get.uid');
            if(!$userID){
                throw new ParamterException([
                    'msg' => '没有指定需要操作的用户对象'
                ]);
            }
            return $userID;
        }else{
            $uid = self::getCurrentTokenVar('uid');
            return $uid;
        }
    }
    
    /**
     * 检查操作的UID是否合法
     * @param $checkedUID
     * @return bool
     * @throws Exception
     */
    public static function isValidOperate($checkedUID){
        if(!$checkedUID){
            throw new Exception('检查UID时必须传入一个被检查的UID');
        }
        $currentOperateUID = self::getCurrentUid();
        if($currentOperateUID == $checkedUID){
            return true;
        }else{
            return false;
        }
    }
    
    /**
     * 检查token是否存在
     * @param $token
     * @return bool
     */
    public static function verifyToken($token){
        $exist = Cache::get($token);
        if($exist){
            return true;
        }else{
            return false;
        }
    }
    
}